{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 手写数字识别卷积神经网络\n",
    "\n",
    "本文件是为了配合“迁移学习”内容而编写的文件，它的作用是将训练手写识别卷积神经网络，并将其保存在硬盘上以便迁移学习的时候能够读取该文件。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入所需要的包，请保证torchvision已经在你的环境中安装好\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.autograd import Variable\n",
    "import torch.optim as optim\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import torchvision.datasets as dsets\n",
    "import torchvision.transforms as transforms\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Hyper Parameters \n",
    "image_size = 28  #图像的总尺寸28*28\n",
    "num_classes = 10  #标签的种类数\n",
    "num_epochs = 20  #训练的总循环周期\n",
    "batch_size = 64\n",
    "learning_rate = 0.001\n",
    "\n",
    "# 加载MINIST数据，如果没有下载过，就会在当前路径下新建/data子目录，并把文件存放其中\n",
    "\n",
    "train_dataset = dsets.MNIST(root='./data',  #文件存放路径\n",
    "                            train=True,   #提取训练集\n",
    "                            transform=transforms.ToTensor(),  #将图像转化为Tensor\n",
    "                            download=True)\n",
    "\n",
    "test_dataset = dsets.MNIST(root='./data', \n",
    "                           train=False, \n",
    "                           transform=transforms.ToTensor())\n",
    "\n",
    "# 训练数据集的加载器，自动将数据分割成batch，顺序随机打乱\n",
    "train_loader = torch.utils.data.DataLoader(dataset=train_dataset, \n",
    "                                           batch_size=batch_size, \n",
    "                                           shuffle=True)\n",
    "# 测试数据集的加载器，自动将数据分割成batch\n",
    "permutes = np.random.permutation(range(len(test_dataset)))\n",
    "indices_val = permutes[:5000]\n",
    "indices_test = permutes[5000:]\n",
    "sampler_val = torch.utils.data.sampler.SubsetRandomSampler(indices_val)\n",
    "sampler_test = torch.utils.data.sampler.SubsetRandomSampler(indices_test)\n",
    "validation_loader = torch.utils.data.DataLoader(dataset =train_dataset,\n",
    "                                                batch_size = batch_size,\n",
    "                                                shuffle = False,\n",
    "                                                sampler = sampler_val\n",
    "                                               )\n",
    "test_loader = torch.utils.data.DataLoader(dataset=test_dataset, \n",
    "                                          batch_size=batch_size, \n",
    "                                          shuffle=False,\n",
    "                                          sampler = sampler_test\n",
    "                                         )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#定义卷积神经网络：4和8为人为指定的两个卷积层的厚度\n",
    "depth = [4, 8]\n",
    "class ConvNet(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(ConvNet, self).__init__()\n",
    "        self.conv1 = nn.Conv2d(1, 4, 5, padding = 2) #输入通道为1，输出通道为4，窗口大小为5，padding为2\n",
    "        self.pool = nn.MaxPool2d(2, 2) #一个窗口为2*2的pooling运算\n",
    "        self.conv2 = nn.Conv2d(depth[0], depth[1], 5, padding = 2) #第二层卷积，输入通道为depth[0], 输出通道为depth[1]，窗口wei15，padding为2\n",
    "        self.fc1 = nn.Linear(image_size // 4 * image_size // 4 * depth[1] , 512) #一个线性连接层，输入尺寸为最后一层立方体的平铺，输出层512个节点\n",
    "        self.fc2 = nn.Linear(512, num_classes) #最后一层线性分类单元，输入为\n",
    "\n",
    "    def forward(self, x):\n",
    "        #神经网络完成一步前馈运算的过程，从输入到输出\n",
    "        x = F.relu(self.conv1(x))\n",
    "        x = self.pool(x)\n",
    "        x = F.relu(self.conv2(x))\n",
    "        x = self.pool(x)\n",
    "        # 将立体的Tensor全部转换成一维的Tensor。两次pooling操作，所以图像维度减少了1/4\n",
    "        x = x.view(-1, image_size // 4 * image_size // 4 * depth[1])\n",
    "        x = F.relu(self.fc1(x)) #全链接，激活函数\n",
    "        x = F.dropout(x, training=self.training) #以默认为0.5的概率对这一层进行dropout操作\n",
    "        x = self.fc2(x) #全链接，激活函数\n",
    "        x = F.log_softmax(x, dim=1) #log_softmax可以理解为概率对数值\n",
    "        return x\n",
    "    \n",
    "    def retrieve_features(self, x):\n",
    "        #提取卷积神经网络的特征图的函数，返回feature_map1, feature_map2为前两层卷积层的特征图\n",
    "        feature_map1 = F.relu(self.conv1(x))\n",
    "        x = self.pool(feature_map1)\n",
    "        feature_map2 = F.relu(self.conv2(x))\n",
    "        return (feature_map1, feature_map2)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练周期: 0 [0/60000 (0%)]\tLoss: 2.308063\t训练正确率: 14.06%\t校验正确率: 9.16%\n",
      "训练周期: 0 [800/60000 (11%)]\tLoss: 2.295198\t训练正确率: 12.41%\t校验正确率: 21.04%\n",
      "训练周期: 0 [1600/60000 (21%)]\tLoss: 2.228458\t训练正确率: 16.88%\t校验正确率: 32.86%\n",
      "训练周期: 0 [2400/60000 (32%)]\tLoss: 2.038143\t训练正确率: 22.54%\t校验正确率: 50.38%\n",
      "训练周期: 0 [3200/60000 (43%)]\tLoss: 1.212608\t训练正确率: 31.06%\t校验正确率: 76.92%\n",
      "训练周期: 0 [4000/60000 (53%)]\tLoss: 0.861115\t训练正确率: 39.75%\t校验正确率: 83.74%\n",
      "训练周期: 0 [4800/60000 (64%)]\tLoss: 0.692775\t训练正确率: 46.72%\t校验正确率: 87.34%\n",
      "训练周期: 0 [5600/60000 (75%)]\tLoss: 0.484954\t训练正确率: 52.07%\t校验正确率: 88.32%\n",
      "训练周期: 0 [6400/60000 (85%)]\tLoss: 0.509524\t训练正确率: 56.43%\t校验正确率: 90.26%\n",
      "训练周期: 0 [7200/60000 (96%)]\tLoss: 0.547684\t训练正确率: 59.96%\t校验正确率: 91.80%\n",
      "训练周期: 1 [0/60000 (0%)]\tLoss: 0.393884\t训练正确率: 90.62%\t校验正确率: 91.24%\n",
      "训练周期: 1 [800/60000 (11%)]\tLoss: 0.388086\t训练正确率: 89.31%\t校验正确率: 92.24%\n",
      "训练周期: 1 [1600/60000 (21%)]\tLoss: 0.284802\t训练正确率: 89.87%\t校验正确率: 92.28%\n",
      "训练周期: 1 [2400/60000 (32%)]\tLoss: 0.333671\t训练正确率: 90.23%\t校验正确率: 93.02%\n",
      "训练周期: 1 [3200/60000 (43%)]\tLoss: 0.660194\t训练正确率: 90.32%\t校验正确率: 93.20%\n",
      "训练周期: 1 [4000/60000 (53%)]\tLoss: 0.473102\t训练正确率: 90.56%\t校验正确率: 93.94%\n",
      "训练周期: 1 [4800/60000 (64%)]\tLoss: 0.159306\t训练正确率: 90.88%\t校验正确率: 93.76%\n",
      "训练周期: 1 [5600/60000 (75%)]\tLoss: 0.224550\t训练正确率: 91.04%\t校验正确率: 94.40%\n",
      "训练周期: 1 [6400/60000 (85%)]\tLoss: 0.301739\t训练正确率: 91.24%\t校验正确率: 93.90%\n",
      "训练周期: 1 [7200/60000 (96%)]\tLoss: 0.191646\t训练正确率: 91.39%\t校验正确率: 95.04%\n",
      "训练周期: 2 [0/60000 (0%)]\tLoss: 0.222584\t训练正确率: 93.75%\t校验正确率: 94.80%\n",
      "训练周期: 2 [800/60000 (11%)]\tLoss: 0.211901\t训练正确率: 93.07%\t校验正确率: 94.88%\n",
      "训练周期: 2 [1600/60000 (21%)]\tLoss: 0.196847\t训练正确率: 93.52%\t校验正确率: 95.22%\n",
      "训练周期: 2 [2400/60000 (32%)]\tLoss: 0.194103\t训练正确率: 93.54%\t校验正确率: 95.48%\n",
      "训练周期: 2 [3200/60000 (43%)]\tLoss: 0.134303\t训练正确率: 93.45%\t校验正确率: 94.88%\n",
      "训练周期: 2 [4000/60000 (53%)]\tLoss: 0.095012\t训练正确率: 93.48%\t校验正确率: 95.60%\n",
      "训练周期: 2 [4800/60000 (64%)]\tLoss: 0.103834\t训练正确率: 93.57%\t校验正确率: 95.26%\n",
      "训练周期: 2 [5600/60000 (75%)]\tLoss: 0.167961\t训练正确率: 93.69%\t校验正确率: 95.20%\n",
      "训练周期: 2 [6400/60000 (85%)]\tLoss: 0.095374\t训练正确率: 93.74%\t校验正确率: 95.62%\n",
      "训练周期: 2 [7200/60000 (96%)]\tLoss: 0.071416\t训练正确率: 93.83%\t校验正确率: 96.10%\n",
      "训练周期: 3 [0/60000 (0%)]\tLoss: 0.155087\t训练正确率: 95.31%\t校验正确率: 96.08%\n",
      "训练周期: 3 [800/60000 (11%)]\tLoss: 0.108592\t训练正确率: 94.68%\t校验正确率: 96.16%\n",
      "训练周期: 3 [1600/60000 (21%)]\tLoss: 0.203469\t训练正确率: 94.72%\t校验正确率: 95.98%\n",
      "训练周期: 3 [2400/60000 (32%)]\tLoss: 0.188312\t训练正确率: 94.66%\t校验正确率: 96.10%\n",
      "训练周期: 3 [3200/60000 (43%)]\tLoss: 0.068819\t训练正确率: 94.71%\t校验正确率: 96.02%\n",
      "训练周期: 3 [4000/60000 (53%)]\tLoss: 0.519806\t训练正确率: 94.83%\t校验正确率: 96.40%\n",
      "训练周期: 3 [4800/60000 (64%)]\tLoss: 0.071140\t训练正确率: 94.91%\t校验正确率: 96.42%\n",
      "训练周期: 3 [5600/60000 (75%)]\tLoss: 0.254541\t训练正确率: 94.93%\t校验正确率: 96.42%\n",
      "训练周期: 3 [6400/60000 (85%)]\tLoss: 0.211525\t训练正确率: 94.97%\t校验正确率: 96.66%\n",
      "训练周期: 3 [7200/60000 (96%)]\tLoss: 0.121266\t训练正确率: 94.95%\t校验正确率: 96.64%\n",
      "训练周期: 4 [0/60000 (0%)]\tLoss: 0.150669\t训练正确率: 93.75%\t校验正确率: 96.66%\n",
      "训练周期: 4 [800/60000 (11%)]\tLoss: 0.177947\t训练正确率: 95.56%\t校验正确率: 96.76%\n",
      "训练周期: 4 [1600/60000 (21%)]\tLoss: 0.150910\t训练正确率: 95.62%\t校验正确率: 96.44%\n",
      "训练周期: 4 [2400/60000 (32%)]\tLoss: 0.123802\t训练正确率: 95.58%\t校验正确率: 96.56%\n",
      "训练周期: 4 [3200/60000 (43%)]\tLoss: 0.141243\t训练正确率: 95.59%\t校验正确率: 96.46%\n",
      "训练周期: 4 [4000/60000 (53%)]\tLoss: 0.155266\t训练正确率: 95.53%\t校验正确率: 96.78%\n",
      "训练周期: 4 [4800/60000 (64%)]\tLoss: 0.085493\t训练正确率: 95.56%\t校验正确率: 97.02%\n",
      "训练周期: 4 [5600/60000 (75%)]\tLoss: 0.142352\t训练正确率: 95.54%\t校验正确率: 96.90%\n",
      "训练周期: 4 [6400/60000 (85%)]\tLoss: 0.203342\t训练正确率: 95.59%\t校验正确率: 96.90%\n",
      "训练周期: 4 [7200/60000 (96%)]\tLoss: 0.188226\t训练正确率: 95.64%\t校验正确率: 96.88%\n",
      "训练周期: 5 [0/60000 (0%)]\tLoss: 0.136030\t训练正确率: 95.31%\t校验正确率: 96.72%\n",
      "训练周期: 5 [800/60000 (11%)]\tLoss: 0.248767\t训练正确率: 96.12%\t校验正确率: 97.10%\n",
      "训练周期: 5 [1600/60000 (21%)]\tLoss: 0.065982\t训练正确率: 96.09%\t校验正确率: 97.08%\n",
      "训练周期: 5 [2400/60000 (32%)]\tLoss: 0.188226\t训练正确率: 96.07%\t校验正确率: 97.02%\n",
      "训练周期: 5 [3200/60000 (43%)]\tLoss: 0.146660\t训练正确率: 96.00%\t校验正确率: 97.34%\n",
      "训练周期: 5 [4000/60000 (53%)]\tLoss: 0.053541\t训练正确率: 96.01%\t校验正确率: 97.18%\n",
      "训练周期: 5 [4800/60000 (64%)]\tLoss: 0.072912\t训练正确率: 96.06%\t校验正确率: 97.08%\n",
      "训练周期: 5 [5600/60000 (75%)]\tLoss: 0.101650\t训练正确率: 96.02%\t校验正确率: 97.32%\n",
      "训练周期: 5 [6400/60000 (85%)]\tLoss: 0.104066\t训练正确率: 96.02%\t校验正确率: 97.38%\n",
      "训练周期: 5 [7200/60000 (96%)]\tLoss: 0.133064\t训练正确率: 96.01%\t校验正确率: 97.40%\n",
      "训练周期: 6 [0/60000 (0%)]\tLoss: 0.022449\t训练正确率: 100.00%\t校验正确率: 97.22%\n",
      "训练周期: 6 [800/60000 (11%)]\tLoss: 0.027955\t训练正确率: 96.33%\t校验正确率: 97.28%\n",
      "训练周期: 6 [1600/60000 (21%)]\tLoss: 0.098059\t训练正确率: 96.21%\t校验正确率: 97.32%\n",
      "训练周期: 6 [2400/60000 (32%)]\tLoss: 0.109376\t训练正确率: 96.28%\t校验正确率: 97.44%\n",
      "训练周期: 6 [3200/60000 (43%)]\tLoss: 0.096086\t训练正确率: 96.33%\t校验正确率: 97.60%\n",
      "训练周期: 6 [4000/60000 (53%)]\tLoss: 0.143805\t训练正确率: 96.33%\t校验正确率: 97.38%\n",
      "训练周期: 6 [4800/60000 (64%)]\tLoss: 0.108903\t训练正确率: 96.30%\t校验正确率: 97.62%\n",
      "训练周期: 6 [5600/60000 (75%)]\tLoss: 0.039911\t训练正确率: 96.39%\t校验正确率: 97.46%\n",
      "训练周期: 6 [6400/60000 (85%)]\tLoss: 0.100752\t训练正确率: 96.42%\t校验正确率: 97.44%\n",
      "训练周期: 6 [7200/60000 (96%)]\tLoss: 0.048191\t训练正确率: 96.42%\t校验正确率: 97.42%\n",
      "训练周期: 7 [0/60000 (0%)]\tLoss: 0.235149\t训练正确率: 92.19%\t校验正确率: 97.74%\n",
      "训练周期: 7 [800/60000 (11%)]\tLoss: 0.056645\t训练正确率: 96.70%\t校验正确率: 97.72%\n",
      "训练周期: 7 [1600/60000 (21%)]\tLoss: 0.173012\t训练正确率: 96.63%\t校验正确率: 97.68%\n",
      "训练周期: 7 [2400/60000 (32%)]\tLoss: 0.161173\t训练正确率: 96.75%\t校验正确率: 97.72%\n",
      "训练周期: 7 [3200/60000 (43%)]\tLoss: 0.100331\t训练正确率: 96.70%\t校验正确率: 97.76%\n",
      "训练周期: 7 [4000/60000 (53%)]\tLoss: 0.051447\t训练正确率: 96.72%\t校验正确率: 97.54%\n",
      "训练周期: 7 [4800/60000 (64%)]\tLoss: 0.101000\t训练正确率: 96.76%\t校验正确率: 97.74%\n",
      "训练周期: 7 [5600/60000 (75%)]\tLoss: 0.208165\t训练正确率: 96.79%\t校验正确率: 97.62%\n",
      "训练周期: 7 [6400/60000 (85%)]\tLoss: 0.072879\t训练正确率: 96.79%\t校验正确率: 97.58%\n",
      "训练周期: 7 [7200/60000 (96%)]\tLoss: 0.049044\t训练正确率: 96.78%\t校验正确率: 97.78%\n",
      "训练周期: 8 [0/60000 (0%)]\tLoss: 0.138968\t训练正确率: 93.75%\t校验正确率: 97.82%\n",
      "训练周期: 8 [800/60000 (11%)]\tLoss: 0.042328\t训练正确率: 96.33%\t校验正确率: 97.68%\n",
      "训练周期: 8 [1600/60000 (21%)]\tLoss: 0.038114\t训练正确率: 96.60%\t校验正确率: 97.84%\n",
      "训练周期: 8 [2400/60000 (32%)]\tLoss: 0.052576\t训练正确率: 96.76%\t校验正确率: 97.84%\n",
      "训练周期: 8 [3200/60000 (43%)]\tLoss: 0.156408\t训练正确率: 96.77%\t校验正确率: 97.66%\n",
      "训练周期: 8 [4000/60000 (53%)]\tLoss: 0.140307\t训练正确率: 96.81%\t校验正确率: 97.94%\n",
      "训练周期: 8 [4800/60000 (64%)]\tLoss: 0.200441\t训练正确率: 96.85%\t校验正确率: 97.60%\n",
      "训练周期: 8 [5600/60000 (75%)]\tLoss: 0.058010\t训练正确率: 96.91%\t校验正确率: 97.88%\n",
      "训练周期: 8 [6400/60000 (85%)]\tLoss: 0.043511\t训练正确率: 96.89%\t校验正确率: 98.04%\n",
      "训练周期: 8 [7200/60000 (96%)]\tLoss: 0.151310\t训练正确率: 96.92%\t校验正确率: 97.98%\n",
      "训练周期: 9 [0/60000 (0%)]\tLoss: 0.052444\t训练正确率: 98.44%\t校验正确率: 97.86%\n",
      "训练周期: 9 [800/60000 (11%)]\tLoss: 0.048289\t训练正确率: 97.06%\t校验正确率: 97.94%\n",
      "训练周期: 9 [1600/60000 (21%)]\tLoss: 0.076331\t训练正确率: 97.11%\t校验正确率: 98.08%\n",
      "训练周期: 9 [2400/60000 (32%)]\tLoss: 0.076029\t训练正确率: 97.21%\t校验正确率: 97.84%\n",
      "训练周期: 9 [3200/60000 (43%)]\tLoss: 0.109260\t训练正确率: 97.17%\t校验正确率: 97.96%\n",
      "训练周期: 9 [4000/60000 (53%)]\tLoss: 0.049331\t训练正确率: 97.03%\t校验正确率: 98.04%\n",
      "训练周期: 9 [4800/60000 (64%)]\tLoss: 0.089401\t训练正确率: 97.05%\t校验正确率: 97.96%\n",
      "训练周期: 9 [5600/60000 (75%)]\tLoss: 0.170307\t训练正确率: 97.03%\t校验正确率: 98.00%\n",
      "训练周期: 9 [6400/60000 (85%)]\tLoss: 0.232684\t训练正确率: 97.01%\t校验正确率: 98.26%\n",
      "训练周期: 9 [7200/60000 (96%)]\tLoss: 0.112176\t训练正确率: 97.04%\t校验正确率: 98.06%\n",
      "训练周期: 10 [0/60000 (0%)]\tLoss: 0.050034\t训练正确率: 98.44%\t校验正确率: 97.98%\n",
      "训练周期: 10 [800/60000 (11%)]\tLoss: 0.101688\t训练正确率: 97.12%\t校验正确率: 98.00%\n",
      "训练周期: 10 [1600/60000 (21%)]\tLoss: 0.029659\t训练正确率: 97.16%\t校验正确率: 98.22%\n",
      "训练周期: 10 [2400/60000 (32%)]\tLoss: 0.336376\t训练正确率: 97.23%\t校验正确率: 98.28%\n",
      "训练周期: 10 [3200/60000 (43%)]\tLoss: 0.428553\t训练正确率: 97.25%\t校验正确率: 98.08%\n",
      "训练周期: 10 [4000/60000 (53%)]\tLoss: 0.052464\t训练正确率: 97.21%\t校验正确率: 98.32%\n",
      "训练周期: 10 [4800/60000 (64%)]\tLoss: 0.083153\t训练正确率: 97.17%\t校验正确率: 98.16%\n",
      "训练周期: 10 [5600/60000 (75%)]\tLoss: 0.090994\t训练正确率: 97.16%\t校验正确率: 98.18%\n",
      "训练周期: 10 [6400/60000 (85%)]\tLoss: 0.074663\t训练正确率: 97.10%\t校验正确率: 98.20%\n",
      "训练周期: 10 [7200/60000 (96%)]\tLoss: 0.082484\t训练正确率: 97.14%\t校验正确率: 98.18%\n",
      "训练周期: 11 [0/60000 (0%)]\tLoss: 0.039812\t训练正确率: 100.00%\t校验正确率: 98.12%\n",
      "训练周期: 11 [800/60000 (11%)]\tLoss: 0.151489\t训练正确率: 97.49%\t校验正确率: 98.24%\n",
      "训练周期: 11 [1600/60000 (21%)]\tLoss: 0.114078\t训练正确率: 97.43%\t校验正确率: 98.34%\n",
      "训练周期: 11 [2400/60000 (32%)]\tLoss: 0.076015\t训练正确率: 97.36%\t校验正确率: 98.24%\n",
      "训练周期: 11 [3200/60000 (43%)]\tLoss: 0.189340\t训练正确率: 97.42%\t校验正确率: 98.28%\n",
      "训练周期: 11 [4000/60000 (53%)]\tLoss: 0.075320\t训练正确率: 97.40%\t校验正确率: 98.24%\n",
      "训练周期: 11 [4800/60000 (64%)]\tLoss: 0.137504\t训练正确率: 97.32%\t校验正确率: 98.62%\n",
      "训练周期: 11 [5600/60000 (75%)]\tLoss: 0.118159\t训练正确率: 97.28%\t校验正确率: 98.42%\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练周期: 11 [6400/60000 (85%)]\tLoss: 0.052375\t训练正确率: 97.31%\t校验正确率: 98.32%\n",
      "训练周期: 11 [7200/60000 (96%)]\tLoss: 0.070787\t训练正确率: 97.33%\t校验正确率: 98.24%\n",
      "训练周期: 12 [0/60000 (0%)]\tLoss: 0.154432\t训练正确率: 95.31%\t校验正确率: 98.38%\n",
      "训练周期: 12 [800/60000 (11%)]\tLoss: 0.136317\t训练正确率: 97.73%\t校验正确率: 98.44%\n",
      "训练周期: 12 [1600/60000 (21%)]\tLoss: 0.065930\t训练正确率: 97.57%\t校验正确率: 98.34%\n",
      "训练周期: 12 [2400/60000 (32%)]\tLoss: 0.055034\t训练正确率: 97.51%\t校验正确率: 98.10%\n",
      "训练周期: 12 [3200/60000 (43%)]\tLoss: 0.052706\t训练正确率: 97.46%\t校验正确率: 98.46%\n",
      "训练周期: 12 [4000/60000 (53%)]\tLoss: 0.040120\t训练正确率: 97.48%\t校验正确率: 98.34%\n",
      "训练周期: 12 [4800/60000 (64%)]\tLoss: 0.070722\t训练正确率: 97.48%\t校验正确率: 98.52%\n",
      "训练周期: 12 [5600/60000 (75%)]\tLoss: 0.043985\t训练正确率: 97.51%\t校验正确率: 98.36%\n",
      "训练周期: 12 [6400/60000 (85%)]\tLoss: 0.146802\t训练正确率: 97.47%\t校验正确率: 98.50%\n",
      "训练周期: 12 [7200/60000 (96%)]\tLoss: 0.066882\t训练正确率: 97.47%\t校验正确率: 98.40%\n",
      "训练周期: 13 [0/60000 (0%)]\tLoss: 0.044242\t训练正确率: 96.88%\t校验正确率: 98.60%\n",
      "训练周期: 13 [800/60000 (11%)]\tLoss: 0.077500\t训练正确率: 97.65%\t校验正确率: 98.42%\n",
      "训练周期: 13 [1600/60000 (21%)]\tLoss: 0.029833\t训练正确率: 97.51%\t校验正确率: 98.46%\n",
      "训练周期: 13 [2400/60000 (32%)]\tLoss: 0.137675\t训练正确率: 97.53%\t校验正确率: 98.42%\n",
      "训练周期: 13 [3200/60000 (43%)]\tLoss: 0.138075\t训练正确率: 97.57%\t校验正确率: 98.62%\n",
      "训练周期: 13 [4000/60000 (53%)]\tLoss: 0.050124\t训练正确率: 97.60%\t校验正确率: 98.28%\n",
      "训练周期: 13 [4800/60000 (64%)]\tLoss: 0.116116\t训练正确率: 97.62%\t校验正确率: 98.56%\n",
      "训练周期: 13 [5600/60000 (75%)]\tLoss: 0.076905\t训练正确率: 97.59%\t校验正确率: 98.30%\n",
      "训练周期: 13 [6400/60000 (85%)]\tLoss: 0.115691\t训练正确率: 97.60%\t校验正确率: 98.44%\n",
      "训练周期: 13 [7200/60000 (96%)]\tLoss: 0.101864\t训练正确率: 97.62%\t校验正确率: 98.30%\n",
      "训练周期: 14 [0/60000 (0%)]\tLoss: 0.048460\t训练正确率: 98.44%\t校验正确率: 98.40%\n",
      "训练周期: 14 [800/60000 (11%)]\tLoss: 0.019644\t训练正确率: 97.69%\t校验正确率: 98.54%\n",
      "训练周期: 14 [1600/60000 (21%)]\tLoss: 0.165562\t训练正确率: 97.74%\t校验正确率: 98.48%\n",
      "训练周期: 14 [2400/60000 (32%)]\tLoss: 0.058784\t训练正确率: 97.75%\t校验正确率: 98.60%\n",
      "训练周期: 14 [3200/60000 (43%)]\tLoss: 0.062749\t训练正确率: 97.75%\t校验正确率: 98.46%\n",
      "训练周期: 14 [4000/60000 (53%)]\tLoss: 0.089423\t训练正确率: 97.84%\t校验正确率: 98.62%\n",
      "训练周期: 14 [4800/60000 (64%)]\tLoss: 0.058724\t训练正确率: 97.76%\t校验正确率: 98.26%\n",
      "训练周期: 14 [5600/60000 (75%)]\tLoss: 0.018819\t训练正确率: 97.74%\t校验正确率: 98.78%\n",
      "训练周期: 14 [6400/60000 (85%)]\tLoss: 0.126533\t训练正确率: 97.70%\t校验正确率: 98.58%\n",
      "训练周期: 14 [7200/60000 (96%)]\tLoss: 0.033474\t训练正确率: 97.71%\t校验正确率: 98.54%\n",
      "训练周期: 15 [0/60000 (0%)]\tLoss: 0.122201\t训练正确率: 95.31%\t校验正确率: 98.40%\n",
      "训练周期: 15 [800/60000 (11%)]\tLoss: 0.036840\t训练正确率: 97.56%\t校验正确率: 98.62%\n",
      "训练周期: 15 [1600/60000 (21%)]\tLoss: 0.064811\t训练正确率: 97.71%\t校验正确率: 98.62%\n",
      "训练周期: 15 [2400/60000 (32%)]\tLoss: 0.119623\t训练正确率: 97.65%\t校验正确率: 98.60%\n",
      "训练周期: 15 [3200/60000 (43%)]\tLoss: 0.232780\t训练正确率: 97.72%\t校验正确率: 98.50%\n",
      "训练周期: 15 [4000/60000 (53%)]\tLoss: 0.034899\t训练正确率: 97.78%\t校验正确率: 98.62%\n",
      "训练周期: 15 [4800/60000 (64%)]\tLoss: 0.042046\t训练正确率: 97.78%\t校验正确率: 98.66%\n",
      "训练周期: 15 [5600/60000 (75%)]\tLoss: 0.058352\t训练正确率: 97.78%\t校验正确率: 98.68%\n",
      "训练周期: 15 [6400/60000 (85%)]\tLoss: 0.038761\t训练正确率: 97.77%\t校验正确率: 98.50%\n",
      "训练周期: 15 [7200/60000 (96%)]\tLoss: 0.023932\t训练正确率: 97.76%\t校验正确率: 98.64%\n",
      "训练周期: 16 [0/60000 (0%)]\tLoss: 0.061801\t训练正确率: 98.44%\t校验正确率: 98.62%\n",
      "训练周期: 16 [800/60000 (11%)]\tLoss: 0.072842\t训练正确率: 97.83%\t校验正确率: 98.52%\n",
      "训练周期: 16 [1600/60000 (21%)]\tLoss: 0.065697\t训练正确率: 97.75%\t校验正确率: 98.54%\n",
      "训练周期: 16 [2400/60000 (32%)]\tLoss: 0.021114\t训练正确率: 97.90%\t校验正确率: 98.68%\n",
      "训练周期: 16 [3200/60000 (43%)]\tLoss: 0.035017\t训练正确率: 97.93%\t校验正确率: 98.62%\n",
      "训练周期: 16 [4000/60000 (53%)]\tLoss: 0.078584\t训练正确率: 97.91%\t校验正确率: 98.66%\n",
      "训练周期: 16 [4800/60000 (64%)]\tLoss: 0.080150\t训练正确率: 97.92%\t校验正确率: 98.54%\n",
      "训练周期: 16 [5600/60000 (75%)]\tLoss: 0.288427\t训练正确率: 97.89%\t校验正确率: 98.80%\n",
      "训练周期: 16 [6400/60000 (85%)]\tLoss: 0.025009\t训练正确率: 97.87%\t校验正确率: 98.62%\n",
      "训练周期: 16 [7200/60000 (96%)]\tLoss: 0.040247\t训练正确率: 97.90%\t校验正确率: 98.82%\n",
      "训练周期: 17 [0/60000 (0%)]\tLoss: 0.007645\t训练正确率: 100.00%\t校验正确率: 98.84%\n",
      "训练周期: 17 [800/60000 (11%)]\tLoss: 0.035465\t训练正确率: 98.13%\t校验正确率: 98.74%\n",
      "训练周期: 17 [1600/60000 (21%)]\tLoss: 0.048809\t训练正确率: 98.13%\t校验正确率: 98.78%\n",
      "训练周期: 17 [2400/60000 (32%)]\tLoss: 0.011508\t训练正确率: 98.15%\t校验正确率: 98.66%\n",
      "训练周期: 17 [3200/60000 (43%)]\tLoss: 0.033125\t训练正确率: 98.16%\t校验正确率: 98.62%\n",
      "训练周期: 17 [4000/60000 (53%)]\tLoss: 0.113653\t训练正确率: 98.08%\t校验正确率: 98.64%\n",
      "训练周期: 17 [4800/60000 (64%)]\tLoss: 0.023853\t训练正确率: 98.03%\t校验正确率: 98.60%\n",
      "训练周期: 17 [5600/60000 (75%)]\tLoss: 0.016369\t训练正确率: 98.00%\t校验正确率: 98.60%\n",
      "训练周期: 17 [6400/60000 (85%)]\tLoss: 0.022845\t训练正确率: 98.00%\t校验正确率: 98.84%\n",
      "训练周期: 17 [7200/60000 (96%)]\tLoss: 0.011031\t训练正确率: 98.02%\t校验正确率: 98.86%\n",
      "训练周期: 18 [0/60000 (0%)]\tLoss: 0.078859\t训练正确率: 96.88%\t校验正确率: 98.62%\n",
      "训练周期: 18 [800/60000 (11%)]\tLoss: 0.161500\t训练正确率: 98.00%\t校验正确率: 98.54%\n",
      "训练周期: 18 [1600/60000 (21%)]\tLoss: 0.124107\t训练正确率: 98.08%\t校验正确率: 98.72%\n",
      "训练周期: 18 [2400/60000 (32%)]\tLoss: 0.050643\t训练正确率: 97.95%\t校验正确率: 98.78%\n",
      "训练周期: 18 [3200/60000 (43%)]\tLoss: 0.043886\t训练正确率: 98.01%\t校验正确率: 98.78%\n",
      "训练周期: 18 [4000/60000 (53%)]\tLoss: 0.086839\t训练正确率: 98.04%\t校验正确率: 98.64%\n",
      "训练周期: 18 [4800/60000 (64%)]\tLoss: 0.012007\t训练正确率: 98.06%\t校验正确率: 98.74%\n",
      "训练周期: 18 [5600/60000 (75%)]\tLoss: 0.058544\t训练正确率: 98.06%\t校验正确率: 98.80%\n",
      "训练周期: 18 [6400/60000 (85%)]\tLoss: 0.083836\t训练正确率: 98.08%\t校验正确率: 98.50%\n",
      "训练周期: 18 [7200/60000 (96%)]\tLoss: 0.037382\t训练正确率: 98.07%\t校验正确率: 98.82%\n",
      "训练周期: 19 [0/60000 (0%)]\tLoss: 0.040831\t训练正确率: 98.44%\t校验正确率: 98.88%\n",
      "训练周期: 19 [800/60000 (11%)]\tLoss: 0.071471\t训练正确率: 98.33%\t校验正确率: 98.76%\n",
      "训练周期: 19 [1600/60000 (21%)]\tLoss: 0.024544\t训练正确率: 98.17%\t校验正确率: 98.78%\n",
      "训练周期: 19 [2400/60000 (32%)]\tLoss: 0.033168\t训练正确率: 98.08%\t校验正确率: 98.92%\n",
      "训练周期: 19 [3200/60000 (43%)]\tLoss: 0.007613\t训练正确率: 98.02%\t校验正确率: 98.82%\n",
      "训练周期: 19 [4000/60000 (53%)]\tLoss: 0.159849\t训练正确率: 98.13%\t校验正确率: 98.84%\n",
      "训练周期: 19 [4800/60000 (64%)]\tLoss: 0.090174\t训练正确率: 98.10%\t校验正确率: 98.80%\n",
      "训练周期: 19 [5600/60000 (75%)]\tLoss: 0.151672\t训练正确率: 98.12%\t校验正确率: 98.82%\n",
      "训练周期: 19 [6400/60000 (85%)]\tLoss: 0.053904\t训练正确率: 98.08%\t校验正确率: 98.78%\n",
      "训练周期: 19 [7200/60000 (96%)]\tLoss: 0.148683\t训练正确率: 98.09%\t校验正确率: 98.96%\n"
     ]
    }
   ],
   "source": [
    "def rightness(predictions, labels):\n",
    "    \"\"\"计算预测错误率的函数，其中predictions是模型给出的一组预测结果，batch_size行10列的矩阵，labels是数据之中的正确答案\"\"\"\n",
    "    pred = torch.max(predictions.data, 1)[1] # 对于任意一行（一个样本）的输出值的第1个维度，求最大，得到每一行的最大元素的下标\n",
    "    rights = pred.eq(labels.data.view_as(pred)).sum() #将下标与labels中包含的类别进行比较，并累计得到比较正确的数量\n",
    "    return rights, len(labels) #返回正确的数量和这一次一共比较了多少元素\n",
    "net = ConvNet() #新建一个卷积神经网络的实例\n",
    "\n",
    "criterion = nn.CrossEntropyLoss() #Loss函数的定义\n",
    "optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) #定义优化器\n",
    "\n",
    "record = [] #记录准确率等数值的容器\n",
    "weights = [] #每若干步就记录一次卷积核\n",
    "\n",
    "#开始训练循环\n",
    "for epoch in range(num_epochs):\n",
    "    \n",
    "    train_rights = [] #记录训练数据集准确率的容器\n",
    "    for batch_idx, (data, target) in enumerate(train_loader):  #针对容器中的每一个批进行循环\n",
    "        data, target = Variable(data), Variable(target) #将Tensor转化为Variable，data为图像，target为标签\n",
    "        net.train() # 给网络模型做标记，标志说模型在训练集上训练\n",
    "        output = net(data) #完成一次预测\n",
    "        loss = criterion(output, target) #计算误差\n",
    "        optimizer.zero_grad() #清空梯度\n",
    "        loss.backward() #反向传播\n",
    "        optimizer.step() #一步随机梯度下降\n",
    "        right = rightness(output, target) #计算准确率所需数值，返回正确的数值为（正确样例数，总样本数）\n",
    "        train_rights.append(right) #将计算结果装到列表容器中\n",
    "\n",
    "    \n",
    "        if batch_idx % 100 == 0: #每间隔100个batch执行一次\n",
    "            \n",
    "            #train_r为一个二元组，分别记录训练集中分类正确的数量和该集合中总的样本数\n",
    "            train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))\n",
    "            \n",
    "            net.eval() # 给网络模型做标记，标志说模型在训练集上训练\n",
    "            val_rights = [] #记录校验数据集准确率的容器\n",
    "            for (data, target) in validation_loader:\n",
    "                data, target = Variable(data), Variable(target)\n",
    "                output = net(data) #完成一次预测\n",
    "                right = rightness(output, target) #计算准确率所需数值，返回正确的数值为（正确样例数，总样本数）\n",
    "                val_rights.append(right)\n",
    "            \n",
    "            #val_r为一个二元组，分别记录校验集中分类正确的数量和该集合中总的样本数\n",
    "            val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))\n",
    "            \n",
    "            #打印准确率等数值，其中正确率为本训练周期Epoch开始后到目前撮的正确率的平均值\n",
    "            print('训练周期: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}\\t训练正确率: {:.2f}%\\t校验正确率: {:.2f}%'.format(\n",
    "                epoch, batch_idx * len(data), len(train_loader.dataset),\n",
    "                100. * batch_idx / len(train_loader), loss.data[0], \n",
    "                100. * train_r[0] / train_r[1], \n",
    "                100. * val_r[0] / val_r[1]))\n",
    "            \n",
    "            #将准确率和权重等数值加载到容器中，以方便后续处理\n",
    "            record.append((100 - 100. * train_r[0] / train_r[1], 100 - 100. * val_r[0] / val_r[1]))\n",
    "            weights.append([net.conv1.weight.data.clone(), net.conv1.bias.data.clone(), \n",
    "                            net.conv2.weight.data.clone(), net.conv2.bias.data.clone()])\n",
    "            "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 保存模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/jake/.conda/envs/torch_py36/lib/python3.6/site-packages/torch/serialization.py:158: UserWarning: Couldn't retrieve source code for container of type ConvNet. It won't be checked for correctness upon loading.\n",
      "  \"type \" + obj.__name__ + \". It won't be checked \"\n"
     ]
    }
   ],
   "source": [
    "# 在这里保存已经训练好的神经网络\n",
    "torch.save(net, 'minst_conv_checkpoint') #后面的字符串为保存文件的路径"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fd77551fcc0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#提取第一层卷积层的卷积核\n",
    "plt.figure(figsize = (10, 7))\n",
    "for i in range(4):\n",
    "    plt.subplot(1,4,i + 1)\n",
    "    plt.imshow(net.conv1.weight.data.numpy()[i,0,...])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fd77551f7f0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 绘制第二层的卷积核\n",
    "plt.figure(figsize = (15, 10))\n",
    "for i in range(4):\n",
    "    for j in range(8):\n",
    "        plt.subplot(4, 8, i * 8 + j + 1)\n",
    "        plt.imshow(net.conv2.weight.data.numpy()[j, i,...])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "256px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
